﻿using ErrorOr;
using Gym.Domain.Common;
using Gym.Domain.Common.Interfaces;
using Gym.Domain.Common.ValueObjects;
using Gym.Domain.ParticipantAggregate;
using Gym.Domain.RoomAggregate;
using Gym.Domain.SessionAggregate.Events;

namespace Gym.Domain.SessionAggregate
{
    public class Session : AggregateRoot
    {        
        
        private readonly List<Reservation> _reservations = new();
        private readonly List<SessionCategory> _categories = new();

        public int NumParticipants => _reservations.Count;
        public DateOnly Date { get; }
        public TimeRange Time { get; } = null!;
        public string Name { get; } = null!;
        public string Description { get; } = null!;
        public int MaxParticipants { get; }
        public Guid RoomId { get;}        
        public IReadOnlyList<SessionCategory> Categories => _categories;

        public Guid TrainerId { get; }

        public Session(
            string name,
            string description,
            int maxParticipants,
            Guid roomId,
            Guid trainerId,
            DateOnly date,
            TimeRange time,
            List<SessionCategory> categories,            
            Guid? id = null) : base(id ?? Guid.NewGuid())
        {
            Name = name;
            Description = description;
            MaxParticipants = maxParticipants;
            RoomId = roomId;
            TrainerId = trainerId;            
            Date = date;
            Time = time;
            _categories.AddRange(categories);            
        }

        public ErrorOr<Success> CancelReservation(Participant participant, IDateTimeProvider dateTimeProvider)
        {
            var reservation = _reservations.FirstOrDefault(x => x.ParticipantId == participant.Id);
            if (reservation == null)
            {
                return Error.NotFound(description: "未找到预约");
            }

            if (IsPastSession(dateTimeProvider.UtcNow))
            {
                return SessionErrors.CannotCancelPastSession;
            }

            if (IsTooCloseToSession(dateTimeProvider.UtcNow))
            {
                return SessionErrors.CannotCancelReservationTooCloseToSession;
            }
                        
            _reservations.Remove(reservation);

            _domainEvents.Add(new ReservationCanceledEvent(this, reservation));
            
            return Result.Success;
        }

        private bool IsTooCloseToSession(DateTime utcNow)
        {
            const int MinHours = 24;

            return (Date.ToDateTime(Time.Start) - utcNow).TotalHours < MinHours;
        }

        private bool IsPastSession(DateTime utcNow)
        {
            return (Date.ToDateTime(Time.End) - utcNow).TotalHours < 0;
        }

        public ErrorOr<Success> ReserveSpot(Participant participant)
        {
            if (_reservations.Count >= MaxParticipants)
            {
                return SessionErrors.CannotHaveMoreReservationsThanParticipants;
            }

            if (_reservations.Any(reservation => reservation.ParticipantId == participant.Id))
            {
                return Error.Conflict(description: "参与者不能在同一时段预订两次");
            }

            var reservation = new Reservation(participant.Id);
            _reservations.Add(reservation);

            _domainEvents.Add(new SessionSpotReservedEvent(this, reservation));

            return Result.Success;
        }

        public void Cancel()
        {
            _domainEvents.Add(new SessionCanceledEvent(this));
        }

        public bool HasReservationForParticipant(Guid participantId)
        {
            return _reservations.Any(reservation => reservation.ParticipantId == participantId);
        }

        public List<Guid> GetParticipantIds()
        {
            return _reservations.ConvertAll(reservation => reservation.ParticipantId);
        }

        private Session() { }
    }
}
